package xiaojian.toolkit.jmx.model;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import javax.management.MBeanAttributeInfo;
import javax.management.MBeanFeatureInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import xiaojian.toolkit.util.ExceptionUtils;

public class FeaturesLoader {
	private static final Logger LOG = LoggerFactory
			.getLogger(FeaturesLoader.class);
	private final MBeanServerConnection _mbsc;

	public FeaturesLoader(MBeanServerConnection mbsc) {
		this._mbsc = mbsc;
	}

	public FeaturesDescription.Unit[] loadAttributeUnits(String nameExp) {
		List<FeaturesDescription.Unit> units = new ArrayList();
		try {
			Set<ObjectName> objNames = this._mbsc.queryNames(
					ObjectName.getInstance(nameExp), null);
			for (ObjectName on : objNames) {
				MBeanInfo info = this._mbsc.getMBeanInfo(on);
				List<String> names = new ArrayList();

				MBeanAttributeInfo[] attrinfos = info.getAttributes();
				for (MBeanAttributeInfo attrinfo : attrinfos) {
					names.add(attrinfo.getName());
				}
				units.add(new FeaturesDescription.Unit(on.toString(),
						(String[]) names.toArray(new String[0])));
			}
		} catch (Exception e) {
			LOG.error("exception when loadAttributeUnits, detail: {}",
					ExceptionUtils.exception2detail(e));
		}
		return (FeaturesDescription.Unit[]) units
				.toArray(new FeaturesDescription.Unit[0]);
	}

	public AttributesSnapshot loadAttributesSnapshot(FeaturesDescription desc) {
		FeaturesDescription.Unit[] units = desc.getUnits();

		List<AttributesSnapshot.Unit> snapshots = new ArrayList();
		for (FeaturesDescription.Unit unit : units) {
			try {
				snapshots.addAll(loadAttrUnits(unit));
			} catch (Exception e) {
				LOG.error(
						"failed to loadAttributesSnapshot for {}, detail: {}",
						unit, ExceptionUtils.exception2detail(e));
			}
		}
		return new AttributesSnapshot(desc.getName(),
				(AttributesSnapshot.Unit[]) snapshots
						.toArray(new AttributesSnapshot.Unit[0]));
	}

	private List<AttributesSnapshot.Unit> loadAttrUnits(
			FeaturesDescription.Unit unit) throws Exception {
		ObjectName nameExp = ObjectName.getInstance(unit.getObjectName());

		Set<ObjectName> names = this._mbsc.queryNames(nameExp, null);
		List<AttributesSnapshot.Unit> snapshots = new ArrayList();
		for (ObjectName objectName : names) {
			Collection<MBeanAttributeInfo> infos = fetchFeatureInfos(
					objectName, this._mbsc.getMBeanInfo(objectName)
							.getAttributes(), unit.getFeatureNames());

			List<AttributesSnapshot.Unit.AttrValue> attrValues = new ArrayList();
			for (MBeanAttributeInfo attrinfo : infos) {
				try {
					Object value = this._mbsc.getAttribute(objectName,
							attrinfo.getName());
					attrValues.add(new AttributesSnapshot.Unit.AttrValue(
							attrinfo, value));
				} catch (Exception e) {
					LOG.warn(
							"failed to getAttribute for {}/{}, just ignore, detail: {}",
							new Object[] { objectName, attrinfo.getName(),
									ExceptionUtils.exception2detail(e) });
				}
			}
			snapshots
					.add(new AttributesSnapshot.Unit(
							objectName,
							(AttributesSnapshot.Unit.AttrValue[]) attrValues
									.toArray(new AttributesSnapshot.Unit.AttrValue[0])));
		}
		return snapshots;
	}

	public OperationsContext loadOperationsContext(FeaturesDescription desc) {
		FeaturesDescription.Unit[] units = desc.getUnits();

		List<OperationsContext.Unit> ctxs = new ArrayList();
		for (FeaturesDescription.Unit unit : units) {
			try {
				ctxs.addAll(loadOperationUnits(unit));
			} catch (Exception e) {
				LOG.error("failed to loadOperationsContext for {}, detail: {}",
						unit, ExceptionUtils.exception2detail(e));
			}
		}
		return new OperationsContext(desc.getName(),
				(OperationsContext.Unit[]) ctxs
						.toArray(new OperationsContext.Unit[0]));
	}

	private List<OperationsContext.Unit> loadOperationUnits(
			FeaturesDescription.Unit unit) throws Exception {
		ObjectName nameExp = ObjectName.getInstance(unit.getObjectName());

		Set<ObjectName> names = this._mbsc.queryNames(nameExp, null);
		List<OperationsContext.Unit> ctxs = new ArrayList();
		for (ObjectName objectName : names) {
			Collection<MBeanOperationInfo> infos = fetchFeatureInfos(
					objectName, this._mbsc.getMBeanInfo(objectName)
							.getOperations(), unit.getFeatureNames());

			ctxs.add(new OperationsContext.Unit(objectName,
					(MBeanOperationInfo[]) infos
							.toArray(new MBeanOperationInfo[0])));
		}
		return ctxs;
	}

	private <T extends MBeanFeatureInfo> Collection<T> fetchFeatureInfos(
			ObjectName objectName, T[] orgInfoArray, String[] names)
			throws Exception {
		if ((null == names) || (names.length == 0)) {
			return Arrays.asList(orgInfoArray);
		}
		List<T> infos = new ArrayList(orgInfoArray.length);
		for (String attrName : names) {
			T info = name2Info(orgInfoArray, attrName);
			if (null != info) {
				infos.add(info);
			} else {
				LOG.info(
						"can't found feature name {} within objectName {}, ignore",
						attrName, objectName);
			}
		}
		return infos;
	}

	private static <T extends MBeanFeatureInfo> T name2Info(T[] infoArray,
			String name) {
		for (T info : infoArray) {
			if (info.getName().equals(name)) {
				return info;
			}
		}
		return null;
	}
}
